package cn.yzw.jc.trace;

import cn.hutool.core.util.StrUtil;
import cn.yzw.jc.trace.listener.TimeTunnelAdviceListener;
import cn.yzw.jc.trace.util.ParamSupported;
import cn.yzw.jc.trace.util.SummaryUtil;
import com.alibaba.jvm.sandbox.api.Information;
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.annotation.Command;
import com.alibaba.jvm.sandbox.api.listener.ext.Advice;
import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder;
import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
import org.kohsuke.MetaInfServices;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.Map;


/**
 * 时间隧道，记录方法的调用，支持根据 id 进行方法调用回放
 *
 * @author liucan@yzw.cn write on 2024/1/10 15:34
 */
@MetaInfServices(Module.class)
@Information(id = "TimeTunnelModule", mode = {Information.Mode.AGENT}, version = "1.0.0", author = "liucan@yzw.cn")
public class TimeTunnelModule extends ParamSupported implements Module {
    /*
     * 参考命令：
     * ./sandbox.sh -p $(lsof -i tcp:10728 | awk 'NR==2 {print $2}') -d 'TimeTunnelModule/record?class=cn.yzw.*&method=checkEnable'
     * ./sandbox.sh -p $(lsof -i tcp:10728 | awk 'NR==2 {print $2}') -d 'TimeTunnelModule/listAllRecords'
     * ./sandbox.sh -p $(lsof -i tcp:10728 | awk 'NR==2 {print $2}') -d 'TimeTunnelModule/reInvoke?id=1001'
     */

    private static final Logger log = LoggerFactory.getLogger(TimeTunnelModule.class);
    @Resource
    private ModuleEventWatcher moduleEventWatcher;
    private final TimeTunnelAdviceListener timeTunnelAdviceListener = new TimeTunnelAdviceListener();
    /**
     * 开启记录
     */
    @Command("record")
    public void record(Map<String, String> param) {
        try {
            String classPattern = getParameter(param, "class");
            String methodPattern = getParameter(param, "method");
            if (StrUtil.isBlank(classPattern) && StrUtil.isBlank(methodPattern)) {
                log.warn("类匹配模式和方法匹配模式都不能为空 {}", param);
                return;
            }
            log.info("print time tunnel msg：class:" + classPattern + ", method:" + methodPattern);
            new EventWatchBuilder(moduleEventWatcher)
                    .onClass(classPattern)
                    .onBehavior(methodPattern)
                    .onWatching().withCall().withLine()
                    .onWatch(timeTunnelAdviceListener);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Command("reInvoke")
    public void reInvoke(Map<String, String> reqParams) {
        String idStr = reqParams.get("id");
        if (idStr != null && !idStr.isEmpty()) {
            try {
                int id = Integer.parseInt(idStr);
                Advice advice = timeTunnelAdviceListener.getTimePoint(id);
                Object obj = advice.getTarget();
                Object[] params = advice.getParameterArray();
                Object result = advice.getBehavior().invoke(obj, params);
                log.info("流量重放成功 " + ", id: " + id + " " + advice.getBehavior().getName() + ", 参数: " + SummaryUtil.summary(params) + ",返回值:" + result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            log.info("请指定需要触发的id");
        }
    }

    @Command("listAllRecords")
    public void listAllRecords() {
        timeTunnelAdviceListener.listAllRecords();
    }
}
